package org.luxor.commons.swagger.config;

import io.swagger.annotations.Api;
import org.luxor.commons.core.web.IRestStatus;
import org.luxor.commons.core.web.RestStatus;
import org.luxor.commons.swagger.config.properties.SwaggerProperties;
import org.luxor.commons.swagger.entity.SwaggerApiInfo;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import springfox.documentation.builders.*;
import springfox.documentation.schema.ModelRef;
import springfox.documentation.service.*;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spi.service.contexts.SecurityContext;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;

import javax.annotation.Resource;
import java.util.*;

/**
 * Swagger基础配置
 *
 * @author Mr.yan @date 2020/2/16
 */
@Configuration
@EnableSwagger2
@EnableConfigurationProperties(SwaggerProperties.class)
@ConditionalOnProperty(prefix = SwaggerProperties.PREFIX, name = "enabled", havingValue = "true", matchIfMissing = true)
public class SwaggerAutoConfig implements WebMvcConfigurer {

    @Resource
    private SwaggerProperties swaggerProperties;

    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler("swagger-ui.html").addResourceLocations("classpath:/META-INF/resources/");
        registry.addResourceHandler("/webjars/**").addResourceLocations("classpath:/META-INF/resources/webjars/");
    }

    @Bean
    @ConditionalOnMissingBean
    public SwaggerApiInfo apiInfo() {
        SwaggerApiInfo info = new SwaggerApiInfo();
        info.setTitle(swaggerProperties.getTitle());
        info.setDescription(swaggerProperties.getDescription());
        info.setServiceUrl(swaggerProperties.getTermsOfServiceUrl());
        info.setVersion(swaggerProperties.getApiVersion());
        info.setStatuses(RestStatus.values());
        return info;
    }

    @Bean
    public Docket createRestApi(SwaggerApiInfo info) {
        ParameterBuilder aParameterBuilder = new ParameterBuilder();
        List<Parameter> aParameters = new ArrayList<>();
        //启用版本号
        if (swaggerProperties.getApiVersionEnable()) {
            String apiVersion = String.valueOf(info.getVersion());
            aParameterBuilder.name("api-version").description("接口版本号，如1.0").parameterType("header")
                    .defaultValue(apiVersion).required(false).modelRef(new ModelRef("string")).build();
            aParameters.add(aParameterBuilder.build());
        }

        Set products = new HashSet<String>();
        products.add("application/json");
        return new Docket(DocumentationType.SWAGGER_2)
                .enable(swaggerProperties.getEnabled())
                .securitySchemes(Arrays.asList(apiKey()))
                .securityContexts(Arrays.asList(securityContext()))
                .apiInfo(apiInfo(info))
                .useDefaultResponseMessages(false)
                .globalResponseMessage(RequestMethod.OPTIONS, responseMessage(info.getStatuses()))
                .globalOperationParameters(aParameters)
                .produces(products)
                .select()
                .apis(RequestHandlerSelectors.withClassAnnotation(Api.class))
                .paths(PathSelectors.any())
                .build();
    }

    private ApiInfo apiInfo(SwaggerApiInfo swaggerApiInfo) {
        return new ApiInfoBuilder()
                .title(swaggerApiInfo.getTitle())
                .description(swaggerApiInfo.getDescription())
                .termsOfServiceUrl(swaggerApiInfo.getServiceUrl())
                .version(String.valueOf(swaggerApiInfo.getVersion()))
                .build();
    }

    private List<ResponseMessage> responseMessage(IRestStatus[] statuses) {
        final LinkedList<ResponseMessage> list = new LinkedList<>();
        Set<IRestStatus> statusList = new HashSet<>(Arrays.asList(RestStatus.values()));
        if (statuses != null) {
            statusList.addAll(Arrays.asList(statuses));
        }
        for (IRestStatus statusCodes : statusList) {
            final ResponseMessageBuilder builder = new ResponseMessageBuilder();
            final ResponseMessage message = builder
                    .code(statusCodes.code())
                    .message(statusCodes.message())
                    .build();
            list.add(message);
        }
        return list;
    }

    private ApiKey apiKey() {
        return new ApiKey("api_key", "Authorization", "header");
    }

    private SecurityContext securityContext() {
        return SecurityContext.builder()
                .securityReferences(defaultAuth())
                .forPaths(PathSelectors.regex(".*(?<!captcha\\.jpg|login|>)$"))
                .build();
    }

    private List<SecurityReference> defaultAuth() {
        AuthorizationScope[] authorizationScopes = new AuthorizationScope[1];
        authorizationScopes[0] = new AuthorizationScope("global", "accessEverything");
        return Arrays.asList(new SecurityReference("api_key", authorizationScopes));
    }

}